package geppetto.phraseTable.builder;

import geppetto.phraseHMM.phraseExtraction.extractedphrase.ExtractedPhrasePairData;
import geppetto.phraseHMM.phraseExtraction.extractedphrase.ExtractedPhrasePairDataCalc;
import geppetto.phraseHMM.phraseExtraction.extractedphrase.ExtractedPhrasePairDataManager;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;


abstract public class ExtractionFileReader {

	String[] headers;	
    BufferedReader	phraseExtractionReader;
	
	ArrayList<String> sourceArray = new ArrayList<String>();
	ArrayList<String> targetArray = new ArrayList<String>();
	ArrayList<HashMap<String, ExtractedPhrasePairData>> dataArray = new ArrayList<HashMap<String, ExtractedPhrasePairData>>();
	ArrayList<String> remainderArguments = new ArrayList<String>();
		
	protected LinkedList<String> sourceReadBuffer = new LinkedList<String>();
	protected LinkedList<String> targetReadBuffer = new LinkedList<String>();
	protected LinkedList<HashMap<String, ExtractedPhrasePairData>> dataReadBuffer = new LinkedList<HashMap<String, ExtractedPhrasePairData>>();
	protected LinkedList<String> remainderArgumentsReadBuffer = new LinkedList<String>();
	
	public ExtractionFileReader(BufferedReader headerFileReader, BufferedReader phraseExtractionReader) throws IOException {
		super();
		String line = headerFileReader.readLine();
		this.headers = getDataFromExtraction(line);
		this.phraseExtractionReader = phraseExtractionReader;
	}
	
	public ExtractionFileReader(String headerFile, String phraseExtactionFile, String encoding) throws IOException {
		this(new BufferedReader(new InputStreamReader(new FileInputStream(headerFile), encoding)),
		new BufferedReader(new InputStreamReader(new FileInputStream(phraseExtactionFile), encoding)));
	}

	public boolean read() throws IOException{
		sourceArray = new ArrayList<String>();
		targetArray = new ArrayList<String>();
		dataArray = new ArrayList<HashMap<String, ExtractedPhrasePairData>>();	
		remainderArguments = new ArrayList<String>();
		ArrayList<HashMap<String, ExtractedPhrasePairData>> mergeQueue = new ArrayList<HashMap<String,ExtractedPhrasePairData>>();

		int num = fillBuffer();
		for(int i = 0; i < num; i++){
			String source = sourceReadBuffer.pollFirst();
			String target = targetReadBuffer.pollFirst();
			HashMap<String, ExtractedPhrasePairData> dataMap = dataReadBuffer.pollFirst();
			String remainder = remainderArgumentsReadBuffer.pollFirst();
						
			if(shouldMerge(source, target, dataMap)){
				mergeQueue.add(dataMap);				
			}
			else{
				
				if(mergeQueue.size()>0){
					HashMap<String, ExtractedPhrasePairData> lastData = dataArray.get(dataArray.size()-1);
					merge(lastData, mergeQueue);
					mergeQueue = new ArrayList<HashMap<String,ExtractedPhrasePairData>>();
				}
				
				sourceArray.add(source);
				targetArray.add(target);
				dataArray.add(dataMap);
				remainderArguments.add(remainder);
			}
		}
		if(mergeQueue.size()>0){
			HashMap<String, ExtractedPhrasePairData> lastData = dataArray.get(dataArray.size()-1);
			merge(lastData, mergeQueue);
			mergeQueue = new ArrayList<HashMap<String,ExtractedPhrasePairData>>();
		}
		if(num>0){
			return true;
		}
		return false;
	}
	
	public void merge(HashMap<String, ExtractedPhrasePairData> data, ArrayList<HashMap<String, ExtractedPhrasePairData>> mergeQueue){
		for(String key : data.keySet()){
			ArrayList<ExtractedPhrasePairData> mergeFeatureQueueArray = new ArrayList<ExtractedPhrasePairData>();
			for(HashMap<String, ExtractedPhrasePairData> mergeFeatureQueue : mergeQueue){
				mergeFeatureQueueArray.add(mergeFeatureQueue.get(key));
			}
			data.get(key).merge(mergeQueue, mergeFeatureQueueArray);
		}
	}
	
	abstract public boolean stopReading();
		
	public boolean shouldMerge(String source, String target, HashMap<String, ExtractedPhrasePairData> data){
		if(sourceArray.size()<1) return false;
		return sourceArray.get(sourceArray.size()-1).equals(source) && targetArray.get(targetArray.size()-1).equals(target);
	}
	
	public String[] getDataFromExtraction(String line){
		return line.split(" \\|\\|\\| ")[2].split(" - ");
	}
	
	public String getRemainingArguments(String line){
		String[] arguments = line.split(" \\|\\|\\| ");
		String ret = "";
		for(int i = 3 ; i < arguments.length; i++){
			ret += arguments[i];
			if(i!=arguments.length -1){
				 ret+= " ||| ";
			}
		}
		return ret;
	}
	
	public String getSourceFromExtraction(String line){
		return line.split(" \\|\\|\\| ")[0];
	}
	
	public String getTargetFromExtraction(String line){
		return line.split(" \\|\\|\\| ")[1];
	}

	public ArrayList<String> getSourceArray() {
		return sourceArray;
	}

	public ArrayList<String> getTargetArray() {
		return targetArray;
	}

	public ArrayList<String> getRemainderArguments() {
		return remainderArguments;
	}

	public void setRemainderArguments(ArrayList<String> remainderArguments) {
		this.remainderArguments = remainderArguments;
	}

	public ArrayList<HashMap<String, ExtractedPhrasePairData>> getDataArray() {
		return dataArray;
	}
	
	protected int fillBuffer() throws IOException{
		int numToRead = sourceReadBuffer.size();
		String extractedPhrase = phraseExtractionReader.readLine();
		while (extractedPhrase != null) {
			String source = getSourceFromExtraction(extractedPhrase);
			String target = getTargetFromExtraction(extractedPhrase);
			String[] data = getDataFromExtraction(extractedPhrase);		
			String remainder = getRemainingArguments(extractedPhrase);
			HashMap<String, ExtractedPhrasePairData> dataMap = new HashMap<String, ExtractedPhrasePairData>();
			for(int i = 0; i < data.length; i++){
				String header = headers[i];
				ExtractedPhrasePairDataCalc calc = ExtractedPhrasePairDataManager.getData(header);
				if(calc!=null){
					dataMap.put(header, calc.loadFromString(data[i]));
				}
			}
			
			sourceReadBuffer.addLast(source);
			targetReadBuffer.addLast(target);
			dataReadBuffer.addLast(dataMap);
			remainderArgumentsReadBuffer.addLast(remainder);
			if(stopReading()){
				return numToRead;
			}
			numToRead++;
			extractedPhrase = phraseExtractionReader.readLine();
		}
		return numToRead;
	}

}
